package com.gitee.srurl.core.util;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.exceptions.ValidateException;
import cn.hutool.core.lang.mutable.MutablePair;
import cn.hutool.json.JSONObject;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import cn.hutool.jwt.JWTValidator;
import com.gitee.srurl.core.exception.login.LoginExpireException;
import com.gitee.srurl.core.exception.login.TokenExpireException;
import com.gitee.srurl.core.util.login.OperationUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * @author zxw
 * @date 2023/4/9 22:23
 */
@Slf4j
@Component
public class JwtUtil {

    /**
     * remote jwt key
     */
    private static String jwtKey;

    /**
     * jwt 过期时间
     */
    private static Integer jwtExpireTime;

    @Value("${short-url.jwt.key}")
    public void setUserJwtKey(String jwtKey) {
        JwtUtil.jwtKey = jwtKey;
    }

    @Value("${short-url.jwt.expire-time}")
    public void setUserJwtExpireTime(Integer jwtExpireTime) {
        JwtUtil.jwtExpireTime = jwtExpireTime;
    }

    /**
     * 生成token
     *
     * @param payload jwt payload
     * @return 生成的token
     */
    public static MutablePair<String, DateTime> genJwtToken(OperationUser payload) {

        // 过期时间
        DateTime expireTime = DateTime.now().offset(DateField.SECOND, jwtExpireTime);

        String token = JWT.create()
                .addHeaders(null)
                .addPayloads(payloadTransfer(payload))
                .setIssuer("short-url")
                .setKey(jwtKey.getBytes(StandardCharsets.UTF_8))
                .setExpiresAt(expireTime)
                .sign();

        return new MutablePair<>(token, expireTime);
    }

    private static Map<String, Object> payloadTransfer(OperationUser payload) {

        Long userId = payload.getUserId();
        String username = payload.getUsername();
        AssertUtil.nonNull(userId, "用户ID不能为空！");

        Map<String, Object> map = new HashMap<>();
        map.put("userId", userId);
        map.put("username", username);
        map.put("api", payload.getApi());

        return map;
    }

    /**
     * 检查token是否过期
     *
     * @param token 待验证的token
     * @return 是否过期
     */
    public static boolean tokenIsExpire(final String token) {

        try {
            JWTValidator.of(token).validateDate(DateUtil.date());
            return false;
        } catch (ValidateException e) {
            return true;
        }
    }

    /**
     * 解析token 不会验证是否已过期
     *
     * @param token 待解析的token
     * @return 解析结果
     */
    public static OperationUser parseToken(final String token) {

        boolean verity = verity(token, true);
        if (!verity) {
            throw new LoginExpireException();
        }

        JWT jwt = JWTUtil.parseToken(token);
        JWTPayload payload = jwt.getPayload();
        JSONObject claimsJson = payload.getClaimsJson();

        Long userId = claimsJson.getLong("userId");
        String username = claimsJson.getStr("username");
        Boolean api = claimsJson.getBool("api");

//        if (userId == null || StrUtil.isBlank(username)) {
//            log.error("token:{},userId:{},username:{},参数错误！", token, userId, username);
//            throw new LoginExpireException();
//        }

        return new OperationUser(userId, username, api);
    }

    public static OperationUser parseTokenAndVerify(final String token) throws TokenExpireException {

        boolean verity = verity(token, true);
        if (!verity) {
            throw new TokenExpireException();
        }

        return parseToken(token);
    }

    /**
     * 验证token是否有效 默认过期会验证不通过
     *
     * @param token 待验证的token
     * @return 是否验证成功
     */
    public static boolean verity(final String token) {

        return verity(token, true);
    }

    /**
     * 验证token是否有效
     *
     * @param token       待验证的token
     * @param checkExpire 是否验证过期时间
     * @return 是否验证成功
     */
    public static boolean verity(final String token, boolean checkExpire) {

        // 验证token是否有效
        boolean verify = JWTUtil.verify(token, jwtKey.getBytes());
        if (verify) {
            // 如果需要验证是否过期
            if (checkExpire) {
                return !tokenIsExpire(token);
            }
        }

        return verify;
    }
}
